/**********************************************************************************************/
/* FBL_File_Posix.cpp																		  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006															  */
/**********************************************************************************************/

#include <VShared/FBL/publ/Headers/StdAfx.h>

#include <VShared/FBL/publ/Headers/FBL.h>

#if FBL_POSIX_API && !FBL_MAC

#include <VShared/FBL/publ/File_OS/posix/FBL_File_Posix.h>

#include <VShared/FBL/publ/Errors/FBL_Exceptions.h>
#include <VShared/FBL/publ/Util_classes/FBL_StringConversion.h>
#include <VShared/FBL/publ/Util_Paths/FBL_Util_Dll_Win.h>
#include <VShared/FBL/publ/Utilities/FBL_Util_Path.h>

#include <errno.h>
#include <fcntl.h>/* open() and O_XXX flags */
#include <sys/stat.h>/* S_IXXX flags */
//#include <sys/types.h>/* mode_t */
#include <unistd.h>/* close() read() */

#include <stdlib.h>
#include <stdio.h>


/**********************************************************************************************/
FBL_Begin_Namespace

/**********************************************************************************************/
File_Posix::File_Posix( void )
{
	mFd	  = -1;
	mReadOnly = false;
}


/**********************************************************************************************/
File_Posix::~File_Posix( void ) 
{
}


#pragma mark -


/**********************************************************************************************/
// Forse to write on disk all cached information from this file.	
//
void File_Posix::Flush( void )
{
	if (mFd == -1 )
		return;

	if ( fsync( mFd ) == -1)
	{
		throw xOSFileError( (ERROR_TYPE) errno );
	}
}


#pragma mark -


/**********************************************************************************************/
flength	File_Posix::Seek( flength inOff, ushort inFrom )
{ 
	int res = lseek( mFd, inOff, inFrom);  
	if( res == -1  )
		throw xOSFileError( (ERROR_TYPE) errno );

	return res;
}


/**********************************************************************************************/
void File_Posix::DoClose( void )
{

	// -----------------------------
	// in case this file is cached we need remove pages from cache.
	I_AspectHost* pHost = dcast<I_AspectHost*>( this );
	if( pHost )
		pHost->SendMessageToAll( 1 );

	// Close file
	int res = close( mFd );
	
	// Check on error
	if( res == -1 )
	{
		throw xOSFileError( (ERROR_TYPE) errno );
	}

	// clean handle
	mFd = -1;
}


/**********************************************************************************************/
void File_Posix::DoCreate( void )
{
	const char* myfile = get_Path().getBufferA();
	
	// Try create file
	mFd = open( myfile, O_RDWR | O_CREAT|O_EXCL|O_LARGEFILE, S_IRUSR | S_IWUSR );	
	
	// Check if error
	if( mFd  == -1)
	{
		throw xOSFileError( (ERROR_TYPE) errno );
	}
}


/**********************************************************************************************/
void File_Posix::DoOpen( void )
{
	const char* myfile =  get_Path().getBufferA();
	const UChar* fileNameU = get_Path().c_str();

	// Try open as RW
	mFd = open( myfile, O_RDWR|O_LARGEFILE );	
	
	// Check if error then try open it as RO
	if(  mFd == -1 )
	{
		// Check if file is not directory
		if( errno == EISDIR )
		{
			throw xOSFileError( ERR_OS_FILE_NOT_FOUND, fileNameU );
		}

		// try open as RO		
		mFd = open( myfile, O_RDONLY|O_LARGEFILE );
		if( mFd == -1 )
		{
			throw xOSFileError( ERR_OS_FILE_NOT_FOUND, fileNameU );
		}
		else
		{
			mReadOnly = true;
		}
	}
}


/**********************************************************************************************/
void File_Posix::DoPutLength( flength inLength )
{
	Seek( inLength, begin );

	// ! Warning. if inLength < current length the file will cutting to the inLength
	if ( ftruncate( mFd, inLength ) )
		throw xOSFileError( (ERROR_TYPE) errno );
}


/**********************************************************************************************/
flength	File_Posix::DoGetLengthOfClosed( void ) const throw()
{
	struct stat statbuf;
	
	if (stat(get_Path().getBufferA(), &statbuf) != 0) 
		return 0;
	
	return (flength) statbuf.st_size;
}


/**********************************************************************************************/
flength	File_Posix::DoGetLengthOfOpened( void ) const throw()
{
	off_t cur_pos = 0;
	off_t file_size = 0;
	
	cur_pos = lseek(mFd, 0, SEEK_CUR);
	if( cur_pos == -1 )
		return 0;
	
	file_size = lseek(mFd, 0, SEEK_END);
	lseek(mFd, cur_pos, SEEK_SET);
	
	return (flength) file_size;
}


/**********************************************************************************************/
void File_Posix::DoThrowOut( void )
{
	const char* myfile = get_Path().getBufferA();
	
	// Close file
	int res = unlink( myfile );
	
	// Check on error
	if( res == -1 )
		throw xOSFileError( (ERROR_TYPE) errno );
}


/**********************************************************************************************/
ulong File_Posix::DoRead(
	char* 	inBuffer,
	flength	inFrom,
	ulong 	inHowMuch )
{
	FBL_CHECK(inBuffer);

	/* pre: read existing information */  
	FBL_CHECK( inFrom + inHowMuch <= get_Length() );

	/* Set file marker to right position */
	Seek(inFrom, begin);

	// read
	ssize_t res =  read (mFd, inBuffer, inHowMuch);
	if( res == -1 )
	{
		throw xOSFileError( (ERROR_TYPE) errno );
	}
	
	return res == -1 ? 0: (ulong) res;
}


/**********************************************************************************************/
// Finds the given position in the file, then writes a given buffer.
//
ulong File_Posix::DoWrite( 
	const char* inBuffer, 
	flength		inFrom, 
	ulong 		inHowMuch )
{
	ulong writen = 0;
	FBL_CHECK(inBuffer);

	if( mReadOnly )
		FBL_Throw(xOSFileError(ERR_OS_FILE_READ_ONLY));
				
	/* Set file marker to right position */
	Seek(inFrom, begin);

	// write
	ssize_t res =  write (mFd, inBuffer, inHowMuch);
	if( res == -1 )
	{
		//remove me
		char* err = strerror( errno );
		throw xOSFileError( (ERROR_TYPE) errno );
	}
	
	return res == -1? 0: (ulong) res;
}


/**********************************************************************************************/
FBL_End_Namespace

#endif // FBL_POSIX && !FBL_MAC
